/** Create and use a tokenizer.<br>
 * SlowTokenizer1 implements Tokenizer. It can parse code and determine character and token 
 * types.
 * 
 * @author Tobin Chee
 * 
 * Change Log:<br>
 * - 04/07/09 - Tobin Chee<br>
 * - Implemented a working(so far) tokenizer slowTokenizer1<br><br>
 * 
 * - 4/25/09 - Tobin Chee<br>
 * - changed SlowTokenizer1 to properly return type "INDIRECT" for token '%'<br><br>
 * 
 * - 4/26/09 - Tobin Chee<br>
 * - Changed slowTokenizer1 to consider, immediate, indirect, and single_quote tokens as separators<br><br>
 * 
 * - 4/28/09 - Tobin Chee<br>
 * - Fixed tokenizer error causing parseEndOfLine to improperly log errors<br>
 * 
 * 
 */
public class SlowTokenizer1 implements Tokenizer {
	/** String buffer to be shared between methods.
	 */
	private StringBuffer buffer;
	/** Token type initialization to be shared between methods.
	 */
	private Assembler.TokenType bufferType;

	/**
	 * Create a Slow Tokenizer.
	 */
	public SlowTokenizer1() {
		buffer = new StringBuffer();
		bufferType = Assembler.TokenType.EMPTY;
	}

	/**
	 * Find the character type.
	 * 
	 * @param ch
	 *            Character to test
	 * @return Type of character.
	 */
	private Assembler.TokenType getCharType(Character ch) {

		if (ch.compareTo(',') == 0) {
			return Assembler.TokenType.COMMA;
		} else if ((ch.compareTo(' ') == 0) || (ch.compareTo('\t') == 0)) {
			return Assembler.TokenType.WHITESPACE;
		} else if ((ch.compareTo('+') == 0) || (ch.compareTo('-') == 0)
				|| (ch.compareTo('*') == 0) || (ch.compareTo('/') == 0)) {
			return Assembler.TokenType.ARITHMETIC;
		} else if (Character.isLetter(ch)) {
			return Assembler.TokenType.MNEMONIC;
		} else if (Character.isDigit(ch)) {
			return Assembler.TokenType.DATA;
		} else if (ch.compareTo('=') == 0) {
			return Assembler.TokenType.IMMEDIATE;
		} else if (ch.compareTo(':') == 0) {
			return Assembler.TokenType.COMMENT;
		} else if (ch.compareTo('%') == 0) {
			return Assembler.TokenType.INDIRECT;
		} else if (ch.compareTo('\'') == 0) {
			return Assembler.TokenType.SINGLE_QUOTE;
		} else {
			return Assembler.TokenType.ERROR;
		}
	}

	/**
	 * Parse a source line until a separator character has been reached.
	 * 
	 * @param sourceLine
	 *            Source line to parse from.
	 * @param token
	 *            Token that was parsed.
	 * @param type
	 *            Type of token that was parsed.
	 */
	private void parseTillSeparator(StringBuffer sourceLine,
			StringBuffer token, Assembler.TokenType[] type) {

		if (sourceLine.length() > 0) {
			Character ch;
			Assembler.TokenType charType;

			ch = sourceLine.charAt(0);
			sourceLine.deleteCharAt(0);
			charType = getCharType(ch);

			if ((charType != Assembler.TokenType.WHITESPACE)
					&& (charType != Assembler.TokenType.COMMA)
					&& (charType != Assembler.TokenType.ARITHMETIC)
					&& (charType != Assembler.TokenType.INDIRECT)
					&& (charType != Assembler.TokenType.IMMEDIATE)
					&& (charType != Assembler.TokenType.SINGLE_QUOTE)) {
				buffer.append(ch);

				if ((charType != Assembler.TokenType.MNEMONIC)
						&& charType != Assembler.TokenType.DATA) {
					bufferType = Assembler.TokenType.ERROR;
				}
				parseTillSeparator(sourceLine, token, type);
			} else { // if char is separator
				token.append(buffer);
				buffer.delete(0, buffer.length());
				type[0] = bufferType;
				bufferType = Assembler.TokenType.EMPTY;
				sourceLine.insert(0, ch);
			}
		} else {
			token.append(buffer);
			buffer.delete(0, buffer.length());
			type[0] = bufferType;
			bufferType = Assembler.TokenType.EMPTY;
		}
	}
	
	/**
	 * Parse source line of code to remove separators.
	 * 
	 * @param sourceLine
	 *            Source line to be parsed
	 * @param token
	 *            Token that was parsed.
	 * @param type
	 *            Type of token that was parsed.
	 */
	private void parseTillNonSeparator(StringBuffer sourceLine,
			StringBuffer token, Assembler.TokenType[] type) {

		if (sourceLine.length() > 0) {
			Character ch;
			Assembler.TokenType charType;

			ch = sourceLine.charAt(0);
			sourceLine.deleteCharAt(0);
			charType = this.getCharType(ch);

			if (charType == Assembler.TokenType.WHITESPACE) {
				buffer.append(ch);
				parseTillNonSeparator(sourceLine, token, type);
			} else {
				token.append(buffer);
				buffer.delete(0, buffer.length());
				type[0] = bufferType;
				bufferType = Assembler.TokenType.EMPTY;
				sourceLine.insert(0, ch);
			}
		} else {
			token.append(buffer);
			type[0] = bufferType;
			buffer.delete(0, buffer.length());
			bufferType = Assembler.TokenType.EMPTY;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see Tokenizer#getNextToken(java.lang.StringBuffer,
	 * java.lang.StringBuffer, Assembler.TokenType[])
	 */
	public void getNextToken(StringBuffer sourceLine, StringBuffer token,
			Assembler.TokenType[] type) {
		Character ch;
		Assembler.TokenType charType;

		if (token.length() > 0) {
			token.delete(0, token.length() + 1);
		}
		ch = sourceLine.charAt(0);
		sourceLine.deleteCharAt(0);
		charType = this.getCharType(ch);

		if ((charType == Assembler.TokenType.COMMA)
				|| (charType == Assembler.TokenType.ARITHMETIC)
				|| (charType == Assembler.TokenType.IMMEDIATE)
				|| (charType == Assembler.TokenType.SINGLE_QUOTE)
				|| (charType == Assembler.TokenType.INDIRECT)) {
			token.append(ch);
			type[0] = charType;
		} else if ((charType == Assembler.TokenType.MNEMONIC)
				|| (charType == Assembler.TokenType.DATA)
				|| (charType == Assembler.TokenType.ERROR)) {
			buffer.append(ch);
			bufferType = charType;
			this.parseTillSeparator(sourceLine, token, type);
		} else if (charType == Assembler.TokenType.WHITESPACE) {
			buffer.append(ch);
			bufferType = charType;
			this.parseTillNonSeparator(sourceLine, token, type);
		} else if (charType == Assembler.TokenType.COMMENT) {
			// Read till end of line
			token.append(ch);
			token.append(sourceLine);
			type[0] = charType;
		}

		if ((type[0] == Assembler.TokenType.MNEMONIC) && (token.length() > 24)) {
			type[0] = Assembler.TokenType.ERROR;
		}
	}

}
